﻿using System;
using OhioState.AssetManaged;

namespace OhioState.Graphics.Proxy
{
    /// <summary>
    /// This class represents a two-dimensional texture that contains histogram
    /// values, calculated for the given 2D texture file (image file).
    /// </summary>
    public class ImageHistogram : TexImage
    {
        private String imageName;
        private String filename;

        /// <summary>
        /// Create a new image histogram.
        /// </summary>
        /// <param name="name">Name of the histogram texture.</param>
        /// <param name="filename">Filename of the corresponding texture file.</param>
        public ImageHistogram(String name, String filename) : base(name, filename)
        {
            this.imageName = name;
            this.filename = filename;

            this.Width = 256;
            this.Height = 256;

            TexelFormat texelFormat = new TexelFormat();
            texelFormat.Format = ImageFormat.RGBA;
            texelFormat.Type = ByteFormat.BYTE;
            this.TexelType = texelFormat;
        }

        /// <summary>
        /// Returns two-dimensional texture with histogram values. Note that current implementation
        /// fills all the y-dimension with the same values (basically we could use just one dimensional
        /// texture) The second dimension will later be used to store gradient information.
        /// </summary>
        public override void GetTexture(out byte[] texels)
        {
            TexImage image = (TexImage)AssetManager.Instance.Find(typeof(TexImage),
                imageName.Substring(0, imageName.Length - "_histogram".Length));
            if (image == null)
            {
                texels = new byte[this.Width * this.Height * 4];
                return;
            }
            
            byte[] imageValues;
            image.GetTexture(out imageValues);

            double[] bins = new double[256];
            //we're reading in red comonent values, assuming that the image
            //is in grayscale (r, g, b are all the same)
            for (int i = 0; i < imageValues.Length; i += 4)
            {
                bins[imageValues[i]]++;
            }

            double[] cdf = new double[256];
            for (int i = 0; i < 256; i++)
            {
                for (int j = 0; j < i; j++)
                {
                    cdf[i] += bins[j];
                }
            }
            double cdf_min = 255;
            double cdf_max = 0;
            for (int i = 0; i < cdf.Length; i++)
            {
                if (cdf_min > cdf[i]) cdf_min = cdf[i];
                if (cdf_max < cdf[i]) cdf_max = cdf[i];
            }

            int num_values = image.Width * image.Height;//number of pixels in the image
            for (int i = 0; i < 256; i++)
            {
                cdf[i] = 255 * (cdf[i] - cdf_min) / num_values;
            }

            texels = new byte[this.Width * this.Height * 4];
            for (int j = 0; j < 256; j++)
            {
                for (int i = 0; i < 256; i++)
                {
                    texels[4 * 256 * j + i * 4 + 0] = (byte)cdf[i];
                    texels[4 * 256 * j + i * 4 + 1] = (byte)cdf[i];
                    texels[4 * 256 * j + i * 4 + 2] = (byte)cdf[i];
                    texels[4 * 256 * j + i * 4 + 3] = (byte)cdf[i];
                }
            }
        }
    }
}
